#

| Chapter 9 Saving Time on the Command Line |

## 9.16 Command Substitution

A pair of backquotes (````) does command substitution. This is really useful - it lets you use the standard output from one command as arguments to another command.

Here's an example. Assume you want to edit all files in the current directory that contain the word "error." Type this:

    -l
    $ vi `grep -l error *.c`
    3 files to edit
    "bar.c" 254 lines, 28338 characters
       ...
    $

But why does this work? How did we build the incantation above? First, think about how you'd do this without using any special techniques. You'd use grep to find out which commands contain the word "error"; then you'd use vi to edit this list:

>     $ grep error *.c
>     bar.c:  error("input too long");
>     bar.c:  error("input too long");
>     baz.c:  error("data formatted incorrectly");
>     foo.c:  error("can't divide by zero"):
>     foo.c:  error("insufficient memory"):
>     $ vi bar.c baz.c foo.c

Is there any way to compress these into one command? Yes, by using command substitution. First, we need to modify our grep command so that it only produces a list of filenames, rather than filenames and text. That's easy; use grep -l:

>     $ grep -l error *.c
>     bar.c
>     baz.c
>     foo.c

The -l option lists each filename only once, even if many lines in the file match. (This makes me think that grep -l was designed with precisely this application in mind.) Now, we want to edit these files; so we put the grep command inside backquotes, and use it as the argument to vi:

>     $ vi `grep -l error *.c`
>     3 files to edit
>     "bar.c" 254 lines, 28338 characters
>        ...
>     $

You might be wondering about the difference between the "vertical" output from grep, and the "horizontal" way that people usually type arguments on a command line. The shell handles this with no problems. Inside backquotes, both a newline and a space are argument separators.

The list you use with command substitution doesn't have to be filenames. Let's see how to send a mail message (1.33) to all the users logged on to the system now. You want a command line like this:

>     % mail joe lisa franka mondo bozo harpo ...

Getting there takes a little thinking about what UNIX commands you need to run to get the output you want. (This is real "Power Tools" stuff!) To get a list of those users, you could use who (51.4). The who output also lists login time and other information - but you can cut that off with a command like cut (35.14):

>     % who | cut -c1-8
>     joe
>     lisa
>     franka
>     lisa
>     joe
>     mondo
>     joe
>     ...

Some users are logged on more than once. To get a unique list, use sort -u (36.6). You're done. Just put the name-making command line between backquotes:

>     % mail `who | cut -c1-8 | sort -u`

If you aren't sure how this works, replace the command you want to run with echo (8.6):

>     % echo `who | cut -c1-8 | sort -u`
>     bozo franka harpo joe lisa mondo

After using UNIX for awhile, you'll find that this is one of its most useful features. You'll find many situations where you use one command to generate a list of words, then put that command in backquotes and use it as an argument to something else. Sometimes you'll want to nest (45.31) the backquotes - this is where the bash and ksh `$( )` operators (which replace the opening and closing backquote, respectively) come in handy. There are some problems (9.20) with command substitution but you usually won't run into them.

This book has many, many examples of command substitution. Here are some of them: making unique filenames (16.16), removing some files from a list (23.21), counting words (29.6), getting a list of files (15.9), setting your shell prompt (7.4, 7.6, 7.11), setting variables (5.4, 45.30), making a wildcard (15.5), and running a loop (40.2).

- JP

9.15 Throwaway Scripts for Complicated Commands |  | 9.17 Handling Lots of Text with Temporary Files